﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ActivityCollection.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Defines the ActivityCollection type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using Diagnostics.Annotations;

namespace MobilePal.State
{
  #region Imported Namespaces


  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Xml.Serialization;
  using Collections;


  #endregion


  /// <summary>
  /// Describes and manipulates the activity collection.
  /// </summary>
  [XmlRoot("Activities")]
  [UsedImplicitly(ImplicitUseFlags.IncludeMembers)]
  public class ActivityCollection: ObservableCollection<Activity>
  {
    #region Events


    /// <summary>
    /// Occurs when activity categories has changed.
    /// </summary>
    public event EventHandler<ActivityCategoriesEventArgs> ActivityCategoriesChanged;

    /// <summary>
    /// Occurs when activity is in progress flag has changed.
    /// </summary>
    public event EventHandler<ActivityStateEventArgs> ActivityIsInProgressChanged;

    /// <summary>
    /// Occurs when activity is in progress flag is about to change.
    /// </summary>
    public event EventHandler<ActivityStateEventArgs> ActivityIsInProgressChanging;


    #endregion


    #region Indexers


    /// <summary>
    /// Gets the <see cref="MobilePal.State.Activity"/> with the specified name.
    /// </summary>
    /// <value>
    /// </value>
    public Activity this[string name]
    {
      get
      {
        return this.FirstOrDefault(activity => activity.Name == name);
      }
    }


    #endregion


    #region Public Methods


    /// <summary>
    /// Determines whether the collection contains an activity with the specified name.
    /// </summary>
    /// <param name="name">
    /// The name.
    /// </param>
    /// <returns>
    /// <c>true</c> if the collection contains an activity with the specified name; otherwise, <c>false</c>.
    /// </returns>
    public bool Contains(string name)
    {
      return this.FirstOrDefault(activity => activity.Name == name) != null;
    }


    /// <summary>
    /// Gets the in progress activities.
    /// </summary>
    /// <returns>
    /// </returns>
    public List<Activity> GetInProgressActivities()
    {
      var activities = new List<Activity>();

      foreach (Activity activity in this)
      {
        if (activity.IsInProgress)
        {
          activities.Add(activity);
        }
      }

      return activities;
    }


    #endregion


    #region Methods


    /// <summary>
    /// Removes all items from the collection.
    /// </summary>
    protected override void ClearItems()
    {
      foreach (Activity activity in this)
      {
        ProcessRemovedActivity(activity);
      }

      base.ClearItems();
    }


    /// <summary>
    /// Inserts an item into the collection at the specified index.
    /// </summary>
    /// <param name="index">
    /// The zero-based index at which item should be inserted.
    /// </param>
    /// <param name="item">
    /// The object to insert.
    /// </param>
    protected override void InsertItem(int index, Activity item)
    {
      ValidateActivity(item);

      ProcessAddedActivity(item);

      base.InsertItem(index, item);
    }


    /// <summary>
    /// Removes the item at the specified index of the collection.
    /// </summary>
    /// <param name="index">
    /// The zero-based index of the element to remove.
    /// </param>
    protected override void RemoveItem(int index)
    {
      ProcessRemovedActivity(this[index]);

      base.RemoveItem(index);
    }


    /// <summary>
    /// Replaces the element at the specified index.
    /// </summary>
    /// <param name="index">
    /// The zero-based index of the element to replace.
    /// </param>
    /// <param name="item">
    /// The new value for the element at the specified index.
    /// </param>
    protected override void SetItem(int index, Activity item)
    {
      ValidateActivity(item);

      ProcessRemovedActivity(this[index]);
      ProcessAddedActivity(item);

      base.SetItem(index, item);
    }


    private void ActivityCategoryCollectionChanged(object sender, NotifyCollectionChangedEventArgs<ActivityCategory> e)
    {
      if (ActivityCategoriesChanged != null)
      {
        ActivityCategoriesChanged(this, new ActivityCategoriesEventArgs((Activity)sender, e));
      }
    }


    private void ItemIsInProgressChanged(object sender, ActivityStateEventArgs e)
    {
      if (ActivityIsInProgressChanged != null)
      {
        ActivityIsInProgressChanged(this, e);
      }
    }


    private void ItemIsInProgressChanging(object sender, ActivityStateEventArgs e)
    {
      if (ActivityIsInProgressChanging != null)
      {
        ActivityIsInProgressChanging(this, e);
      }
    }


    private void ProcessAddedActivity(Activity activity)
    {
      activity.IsInProgressChanged += ItemIsInProgressChanged;
      activity.IsInProgressChanging += ItemIsInProgressChanging;
      activity.CategoryCollectionChanged += ActivityCategoryCollectionChanged;
    }


    private void ProcessRemovedActivity(Activity activity)
    {
      activity.IsInProgressChanged -= ItemIsInProgressChanged;
      activity.IsInProgressChanging -= ItemIsInProgressChanging;
      activity.CategoryCollectionChanged -= ActivityCategoryCollectionChanged;
    }


    /// <exception cref="InvalidOperationException">
    /// <c>InvalidOperationException</c>.
    /// </exception>
    private void ValidateActivity(Activity item)
    {
      Activity activity = this[item.Name];

      if (activity != null)
      {
        throw new InvalidOperationException("An activity with name '" + item.Name + "' already exists.");
      }
    }


    #endregion
  }
}